#pragma once
#include <brpc/channel.h>
#include <mutex>
#include <string>
#include <vector>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <google/protobuf/service.h>
#include "logger.hpp"

namespace my_im
{
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    class ServiceChannel
    {
    public:
        using ptr = std::shared_ptr<ServiceChannel>; // 这个只能放在这里，上边的话ServiceChannel还没定义呢
        ServiceChannel(const std::string &name) : _service_name(name), _index(0) {}
        // 服务上线一个节点，调用append新增信道
        void append(const std::string &host)
        {
            auto channel = std::make_shared<brpc::Channel>();
            brpc::ChannelOptions options;
            options.max_retry = 3;
            options.timeout_ms = 5;
            options.connect_timeout_ms = -1;
            options.protocol = "baudu_std";
            int ret = channel->Init(host.c_str(), &options);
            if (ret < 0)
            {
                ERR("初始化{}-{}信道失败", _service_name, host);
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _hosts.insert(make_pair(host, channel));
            _channels.push_back(channel);
        }

        // 服务下线了一个节点，调用remove释放信道
        void remove(const std::string &host)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hosts.find(host);
            if (it == _hosts.end())
            {
                WRN("没有找到要删除的{}-{}节点信道信息", _service_name, host);
                return;
            }
            for (auto vit = _channels.begin(); vit != _channels.end(); it++)
            {
                if (*vit == it->second)
                {
                    _channels.erase(vit);
                    break;
                }
            }
            _hosts.erase(it);
        }

        ChannelPtr choose()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _index = _index++ % _channels.size();
            return _channels[_index];
        }

    private:
        std::mutex _mutex;
        int32_t _index;                    // 当前channel的下标（计数器）用来循环分配服务到每个Channel上
        std::string _service_name;         // 服务名称
        std::vector<ChannelPtr> _channels; // 当前服务对应的信道
        std::unordered_map<std::string, ChannelPtr> _hosts;
    };

    class ServiceManager
    {
        using ptr = std::shared_ptr<ServiceChannel>;

    public:
        // 声明我们关注的那些服务器的上下线，不关心的就不需要管理
        void declared(const std::string &service_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _follow_service.find(service_name);
            if (it != _follow_service.end())
                return;
            _follow_service.insert(service_name);
        }
        ChannelPtr choose(const std::string &service_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _services.find(service_name);
            if (it == _services.end())
            {
                WRN("没有提供{}相关服务的主机", service_name);
                return ChannelPtr();
            }
            return it->second->choose();
        }
        void onServiceOnline(const std::string service_name, const std::string &host)
        {
            auto fit = _follow_service.find(service_name);
            if (fit == _follow_service.end())
            {
                DBG("{}-{}服务上线了，但是我们并不关心", service_name, host);
                return;
            }
            ptr service;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _services.find(service_name);
                if (it == _services.end())
                {
                    service = std::make_shared<ServiceChannel>(service_name);
                    _services.insert({service_name, service});
                }
                service = it->second;
                if (!service)
                {
                    ERR("新增{}服务节点管理失败", service_name);
                    return;
                }
                service->append(host);
            }
        }
        void onServiceOffline(const std::string &service_name, const std::string &host)
        {
            auto fit = _follow_service.find(service_name);
            if (fit == _follow_service.end())
            {
                DBG("{}-{}服务下线了，但是我们并不关心", service_name, host);
                return;
            }
            ptr service;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _services.find(service_name);
                if (it == _services.end())
                {
                    WRN("没有找到需要删除{}服务器节点", service_name);
                    return;
                }
                service = it->second;
                service->remove(host);
            }
        }

    private:
        std::mutex _mutex;
        std::unordered_set<std::string> _follow_service;
        std::unordered_map<std::string, ptr> _services;
    };
}